#include "tile/slopinglefttile.hpp"

#include "actor.hpp"
#include "game.hpp"
#include "graphics.hpp"
#include "tile/tilemap.hpp"
#include "util/stringutil.hpp"

namespace nmc
{
	SlopingLeftTile::SlopingLeftTile(const Point& tileMapPosition, TileMap* tileMap)
		:Tile(Tile::SLOPING_LEFT, tileMapPosition, tileMap),
		mSolidRight(true),
		mSolidBottom(true),
		mConnectedToASlopingLeftTile(false),
		mRightEndOfSlope(false),
		mNearARoof(false)
	{

	}

	void SlopingLeftTile::stickToFloor(Actor* actor, Point& velocity)
	{
		const Point actorBCP = actor->getBottomCollisionPoint();
		
		int yCorrection = (getWorldPosition().y + getHeight() - 1 - ((actorBCP.x + velocity.x) - getWorldPosition().x)) - actorBCP.y - 1;
			
		velocity.y = yCorrection;
	}

	void SlopingLeftTile::resolveCollision(Actor* actor, Point& velocity)
	{
		const Point actorBCP = actor->getBottomCollisionPoint();

		Point futurePosition = actor->getPosition() + velocity;

		// If this tile is connected to a sloping left tile
		// then it should be considered an empty tile if the actor
		// moves along the slope of the connected tile so this tile
		// won't stop the actor until the actor is safely on this
		// tile's slope.
		if (mConnectedToASlopingLeftTile
			&& actorBCP.x < getWorldPosition().x // Actor is left of the tile.
			&& actorBCP.y > getWorldPosition().y + getHeight()) // Actor bottom collision center is below the tile.
        {
			return;
		}
        
        if (isPointInTile(actorBCP + velocity)
            && isPointRightOfSlope(actorBCP + velocity))
        {
            stickToFloor(actor, velocity);
            return;
        }

		if (velocity.y > 0
            && isPointRightOfTile(actorBCP + velocity)
			&& actorBCP.y < getWorldPosition().y) // Actor is above the tile.
		{
			int yCorrection = actor->getPosition().y  + velocity.y + actor->getHeight() - getWorldPosition().y;
			velocity.y -= yCorrection;
			return;
		}

        /*
		// If the actor has its bottom collision center below the tile and
		// its bottom collision center left of the tile and
		// tries to move to the right into the tile we have a collision.
		if (velocity.x > 0 // Actor is moving to the right.
			&& actorBCP.x < getWorldPosition().x // Actor has its bottom center collision point left of the tile.
			&& actor->getPosition().x  + actor->getWidth() <= getWorldPosition().x // Actor is left of the tile.
			&& actorBCP.y >= getWorldPosition().y + getHeight() // Actor has its bottom center collision point below the tile.
			&& getWorldPosition().x < actor->getPosition().x + velocity.x + actor->getWidth()) // Actor will be in the tile.
		{
			int xCorrection = actor->getPosition().x + velocity.x + actor->getWidth() - getWorldPosition().x;
			velocity.x -= xCorrection;
			return;
		}

		// If the tile is solid at the bottom and the actor tries to move
		// upwards into the tile we have a collision.
		if (mSolidBottom
			&& velocity.y < 0 // Actor is moving upwards.
			&& actor->getPosition().y >= getWorldPosition().y + getHeight() // Actor is below the tile.
			&& getWorldPosition().y + getHeight() > actor->getPosition().y + velocity.y) // Actor will be in the tile.
		{
			int yCorrection = getWorldPosition().y + getHeight() - (actor->getPosition().y + velocity.y);
			velocity.y += yCorrection;
			return;
		}

		// If the tile is solid to the right and the ator tries to move to the left into
		// the tile we have a collision.
		if (mSolidRight
			&& velocity.x < 0 // Actor is moving to the left.
			&& actor->getPosition().x  >= getWorldPosition().x + getWidth() // Actor right of the tile.
			&& getWorldPosition().x + getWidth() > actor->getPosition().x + velocity.x) // Actor will be in the tile.
		{
			int xCorrection = getWorldPosition().x + getWidth() - (actor->getPosition().x + velocity.x);
			velocity.x += xCorrection;
			return;
		}
        */
	}

    bool SlopingLeftTile::isPointInTile(const Point& point)
    {
        return getWorldPosition().x <= point.x
            && getWorldPosition().y <= point.y
            && getWorldPosition().x + getWidth() >= point.x
            && getWorldPosition().y + getHeight() >= point.y;
    }

    bool SlopingLeftTile::isPointLeftOfSlope(const Point& point)
    {
        int x = point.x - getWorldPosition().x;
        int y = point.y - getWorldPosition().y;

        if (x < 0 || y < 0 || x + y < Tile::SIZE - 1)
        {
            return true;
        }


        return false;
    }

    bool SlopingLeftTile::isPointRightOfSlope(const Point& point)
    {
        return !isPointLeftOfSlope(point);
    }

    bool SlopingLeftTile::isPointLeftOfTile(const Point& point)
    {
        return  getWorldPosition().x > point.x;
    }

    bool SlopingLeftTile::isPointRightOfTile(const Point& point)
    {
        return getWorldPosition().x + getWidth() <= point.x;
    }

    bool SlopingLeftTile::isStandingOnFloor(Actor* actor)
	{
        Point actorBCP = actor->getBottomCollisionPoint();
		return (actorBCP.y - getWorldPosition().y + 1) + actorBCP.x - getWorldPosition().x == getWidth() - 1;
	}

	void SlopingLeftTile::calculateSolidity(TileMap* tileMap)
	{
		Tile* right = tileMap->getTile(getTileMapPosition().x + 1, 
									   getTileMapPosition().y);
		Tile* bottom = tileMap->getTile(getTileMapPosition().x, 
										getTileMapPosition().y + 1);
		Tile* bottomLeft = tileMap->getTile(getTileMapPosition().x - 1,
			                                getTileMapPosition().y + 1);
		Tile* roof = tileMap->getTile(getTileMapPosition().x,
			                          getTileMapPosition().y - 2);

		mSolidBottom = bottom->getType() != Tile::SOLID;

		mSolidRight = right->getType() != Tile::SOLID
					&& right->getType() != Tile::SLOPING_RIGHT;

		mConnectedToASlopingLeftTile = bottomLeft->getType() == Tile::SLOPING_LEFT;
	
		mRightEndOfSlope = right->getType() != Tile::SLOPING_LEFT;

		mNearARoof = roof->getType() == Tile::SOLID;
	}

	void SlopingLeftTile::draw(Graphics* graphics)
	{

	}

	void SlopingLeftTile::drawType(Graphics* graphics)
	{
		graphics->setBlendMode(Graphics::ALPHA);
		graphics->setColor(Color(0xffffff80));
		
		graphics->drawLine(getWorldPosition().x, 
						   getWorldPosition().y + Tile::SIZE - 1,
						   getWorldPosition().x + Tile::SIZE - 1,
						   getWorldPosition().y);
		graphics->drawLine(getWorldPosition().x,
						   getWorldPosition().y + Tile::SIZE - 1,
						   getWorldPosition().x + Tile::SIZE - 1,
						   getWorldPosition().y + Tile::SIZE - 1);
		graphics->drawLine(getWorldPosition().x + Tile::SIZE - 1,
						   getWorldPosition().y,
						   getWorldPosition().x + Tile::SIZE - 1,
						   getWorldPosition().y + Tile::SIZE - 1);
	}

	void SlopingLeftTile::drawSolidity(Graphics* graphics)
	{
		graphics->setBlendMode(Graphics::ALPHA);
  
        graphics->setColor(Color(0xff0000));
		if (mSolidBottom)
		{
			graphics->drawLine(getWorldPosition().x, 
							   getWorldPosition().y + Tile::SIZE - 1, 
							   getWorldPosition().x + Tile::SIZE - 1,
							   getWorldPosition().y + Tile::SIZE - 1);
		}

		if (mSolidRight)
		{
			graphics->drawLine(getWorldPosition().x + Tile::SIZE - 1, 
							   getWorldPosition().y, 
							   getWorldPosition().x + Tile::SIZE - 1,
							   getWorldPosition().y + Tile::SIZE - 1);
		}
       
        graphics->setColor(Color(0x00ff00));
		graphics->drawLine(getWorldPosition().x, 
						   getWorldPosition().y + Tile::SIZE - 1,
						   getWorldPosition().x + Tile::SIZE - 1,
						   getWorldPosition().y);

		Tile::drawSolidity(graphics);
	}
}